﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Diagnostics;
using System.IO.Compression;
using System.Xml.Linq;

namespace Susam
{
    interface IFileParser
    {
        Metadata Parse(String path);
    }

    static class FileParserFactory
    {
        public static IFileParser CreateParser(String path)
        {
            if (path.EndsWith(".epub", StringComparison.CurrentCultureIgnoreCase))
            {
                return new ePubParser();
            }
            else if (path.EndsWith(".pdf", StringComparison.CurrentCultureIgnoreCase))
            {
                return new pdfParser();
            }
            else
            {
                return null;
            }
        }
    }

    class ePubParser : IFileParser
    {
        public Metadata Parse(String path)
        {
            Metadata meta = new Metadata(path);
            meta._file.fileType = "ePub";

            /* TODO: Check if the file is modified or not */

            /* File is new/modified */
            string tempSusamDir = Path.GetTempPath() + "Susam\\";
            System.IO.Directory.CreateDirectory(tempSusamDir);
            string tempFilename = Guid.NewGuid().ToString();
            string tempFilePath = tempSusamDir + tempFilename + ".zip";
            string tempDir = tempSusamDir + tempFilename;
            File.Copy(meta._file.filePath, tempFilePath);
            ZipFile.ExtractToDirectory(tempFilePath, tempDir);

            IEnumerable<string> opfFileList = null;
            try
            {
                opfFileList = Directory.EnumerateFiles(tempDir, "content.opf", SearchOption.AllDirectories);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }

            if (opfFileList == null)
            {
                Debug.WriteLine("Invalid ePub file!");
                // TODO: Remove this section since the next block will throw an exception.
            }
            OPFtoMetadata(ref meta, opfFileList);

            /* Cleanup*/
            File.Delete(tempFilePath);
            Directory.Delete(tempDir, true);

            return meta;
        }

        private void OPFtoMetadata(ref Metadata meta, IEnumerable<string> opfFileList)
        {
            foreach (string opf in opfFileList)
            {
                XElement xElement = XElement.Load(opf);
                XNamespace xNamespace = xElement.Attribute("xmlns") != null ? xElement.Attribute("xmlns").Value : XNamespace.None;
                // Do we need the unique identifier?
                //String uniqueID = xElement.Attribute("unique-identifier").Value;
                var xDoc = XDocument.Load(opf);
                XElement metadata = null;
                try
                {
                    metadata = xElement.Element(xNamespace + "metadata");
                }
                catch (Exception)
                {
                    // TODO:
                    // metadata element is mandatory according to the specification.
                    // If we cannot find it, it's OK to skip the book.
                    throw;
                }

                if (metadata == null)
                {
                    // TODO: Log error
                    return;
                    //throw new ArgumentNullException();
                }

                foreach (var element in metadata.Elements().Where(e => e.Value.Trim() != string.Empty))
                {
                    switch (element.Name.LocalName)
                    {
                        // TODO: Doesn't support more than one title elements
                        case "title": meta.title = element.Value; break;

                        // TODO: Doesn't support displaying the author names in the
                        // order listed in the OPF file
                        case "creator": meta.authorList.Add(element.ToRelator()); break;

                        // TODO: No support for subjects yet
                        case "subject": meta.subjectList.Add(element.Value); break;

                        // TODO: Doesn't support description yet
                        // Specification is not clear as to how many descriptions are allowed
                        case "description": meta.description = element.Value; break;

                        // TODO: Optional arguments are not considered
                        case "publisher": meta.publisherList.Add(element.Value); break;

                        // TODO: Use http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.6
                        // to parse different contributor roles.
                        case "contributor": meta.contributorList.Add(element.ToRelator()); break;

                        // TODO: "date" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.7

                        // TODO: "type" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.8

                        // TODO: "format" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.9

                        // At least one identifier is required by the specification
                        // TODO: Optional attributes are not supported
                        case "identifier": meta.identifierList.Add(element.Value); break;

                        // TODO: "source" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.11

                        // At least one language is required by the specification
                        // TODO: Optional attributes are not supported
                        case "language": meta.languageList.Add(element.Value); break;

                        // TODO: "relation" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.13

                        // TODO: "coverage" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.14

                        // TODO: "rights" element is not supported
                        // http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.2.15

                        /* TODO: Only populate the fields that will be visible in the list view.
                         * When the user wants to see the details, rest of the metadata
                         * can be populated on demand and stored in the database.
                         */
                    }
                }
            }
        }
    }

    class pdfParser : IFileParser
    {
        public Metadata Parse(String path)
        {
            Metadata meta = new Metadata(path);
            meta._file.fileType = "PDF";

            return meta;
        }
    }
}
